/**
 * Writed by yura and mak
 */

package wz.legend.handlers;

import org.apache.log4j.Logger;

import java.awt.TrayIcon.MessageType;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;
import java.util.zip.GZIPInputStream;

import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.io.IOUtils;
import org.cyberneko.html.parsers.DOMParser;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import wz.legend.Context;
import wz.legend.HttpContext;
import wz.legend.Status;
import wz.legend.gui.Tray;
import wz.legend.handlers.farming.FarmingContext;
import wz.legend.util.UriConstants;


public class ZikHandler implements ResponseHandler {
    private static final int TIMEOUT = 3000;

    private static final int DELAY = 1500;

    /**
     * Logger for this class
     */
    private static final Logger LOG = Logger.getLogger(ZikHandler.class);

    private enum State {
        INACTIVE, READY, AREA_1, BACKPACK_1, BACKPACK_2, BACKPACK_3, AREA_2
    }

    private boolean hunting = false;

    private XPath path;

    private long dropTime = Long.MAX_VALUE;

    private int counter;

    private long timeout = 60000;

    private State state = State.READY;

    private Preferences preferenses;

    private SimpleDateFormat format;

    private long time;

    public ZikHandler() {
        LOG.debug("init zik handler");
        path = XPathFactory.newInstance().newXPath();
        preferenses = Preferences.userNodeForPackage(this.getClass());
        format = new SimpleDateFormat("yyyy-MM-dd");
        initCounter();
    }

    private void initCounter() {
        counter = preferenses.getInt(format.format(new Date()), 0);
        if (counter == 0) {
            try {
                preferenses.clear();
            } catch (BackingStoreException e) {
                LOG.debug(e, e);
            }
        }
    }

    private void incCounter() {
        initCounter();
        counter++;
        preferenses.putInt(format.format(new Date()), counter);
    }

    public void handle(HttpContext httpContext, Context context) {

        if (context.getStatus() != Status.FARMING) {
            return;
        }

        LOG.debug("Counter: " + counter);
        if (counter == 500) {
            Tray.getInstance().showMessage(
                    "������ 500 ����������");
            context.setStatus(Status.INACTIVE);
            return;
        }

        String str = httpContext.getRequest().getRequestLine().getUri();
        LOG.debug(">>>>>>>>> " + str);

        switch (state) {
        case READY:
            if (str.contains(UriConstants.CHAT_DATA_PHP)) {
                state = State.INACTIVE;
                getZik(httpContext, context);
                state = State.AREA_1;
                initTimer();
            }
            break;
        case AREA_1:
            LOG.debug("%%%% " + str);
            if (str.contains("http://w1.dwar.ru:80/area.php?error=")) {
                context.setStatus(Status.INACTIVE);
                state = State.READY;
                String message;
                try {
                    message = URLDecoder.decode(str.substring(36),
                            "windows-1251");
                } catch (UnsupportedEncodingException e) {
                    message = "error";
                }
                Tray.getInstance().showMessage(message);
            } else if (str.contains("http://w1.dwar.ru:80/area.php")
                    || isTimeout(TIMEOUT)) {
                openBackpack(httpContext, context);
                state = State.BACKPACK_1;
                initTimer();
                LOG.debug("%%%%");
            }
            break;
        case BACKPACK_1:
            LOG.debug("!!!! " + str);
            if (str.contains("http://w1.dwar.ru:80/user_iframe.php?group=1")
                    || isTimeout(TIMEOUT)) {
                try {
                    Thread.sleep(DELAY);
                } catch (InterruptedException e) {

                }
                openBackpackMisc(httpContext, context);
                state = State.BACKPACK_2;
                initTimer();
                LOG.debug("!!!!");
            }
            break;
        case BACKPACK_2:
            LOG.debug("???? " + str);
            if (str.contains("http://w1.dwar.ru:80/user_iframe.php?group=3")) {
                LOG.debug("????");
                try {
                    Thread.sleep(DELAY);
                } catch (InterruptedException e) {

                }
                dropZik(httpContext, context);
                state = State.BACKPACK_3;
                initTimer();
            } else if (isTimeout(TIMEOUT)) {
                state = State.BACKPACK_1;
            }
            break;
        case BACKPACK_3:
            LOG.debug("#### " + str);
            if (str.contains("http://w1.dwar.ru:80/user_iframe.php?group=3")
                    || isTimeout(TIMEOUT)) {
                openLocation(httpContext, context);
                state = State.AREA_2;
                initTimer();
            }
            break;
        case AREA_2:
            LOG.debug("@@@@ " + str);
            if (str.contains("http://w1.dwar.ru:80/area.php?exit=1")
                    || isTimeout(TIMEOUT)) {
                try {
                    Thread.sleep(DELAY);
                } catch (InterruptedException e) {

                }
                state = State.READY;
                initTimer();
            }
            break;
        default:
            break;
        }

        return;
    }

    private void openLocation(HttpContext httpContext, Context context) {
        context.getScriptStater().doScript("go.location");

    }

    private void openBackpackMisc(HttpContext httpContext, Context context) {
        context.getScriptStater().doScript("backpack.misk");

    }

    private void openBackpack(HttpContext httpContext, Context context) {
        context.getScriptStater().doScript("backpack");

    }

    private void getZik(HttpContext httpContext, Context context) {
        context.getScriptStater().doScript("zik.get");
    }

    private void dropZik(HttpContext httpContext, Context context) {
        try {
            Document document = httpContext.getResponseDocument();
            if (hasMatka(document)) {
                Tray.getInstance().showMessage("Matka was hunted");
                state = State.READY;
                context.setStatus(Status.INACTIVE);
            } else {
                int id = hasZik(document);
                if (id != -1) {
                    context.getScriptStater().doScript("zik.drop",
                            new String[] { Integer.toString(id) });
                    incCounter();
                } else {
                    openBackpackMisc(httpContext, context);
                }
            }
        } catch (Exception e) {
            Tray.getInstance().showMessage(e.toString(), "error", MessageType.ERROR);
            context.setStatus(Status.INACTIVE);
            throw new RuntimeException(e);

        }
    }

    public GetMethod getMethod(String url, HttpContext httpContext) {
        GetMethod getMethod = new GetMethod(url);
        org.apache.http.Header[] headers = httpContext.getRequest()
                .getAllHeaders();
        for (org.apache.http.Header header : headers) {
            if (!"Referer".equals(header.getName())
                    && !"Content-Length".equals(header.getName())) {
                getMethod.addRequestHeader(new Header(header.getName(), header
                        .getValue()));
            }
        }
        return getMethod;
    }

    int hasZik(Document document) {
        try {
            NodeList nodes = (NodeList) path.evaluate("id('item_list')/TABLE",
                    document, XPathConstants.NODESET);
            for (int i = 0; i < nodes.getLength(); i++) {
                Node node = nodes.item(i);
                NamedNodeMap attributes = node.getAttributes();
                for (int j = 0; j < attributes.getLength(); j++) {
                    Node item = attributes.item(j);
                    if ("background".equals(item.getNodeName())
                            && "images/data/artifacts/svetliachok2.gif"
                                    .equals(item.getNodeValue())) {
                        return i;
                    }
                }
            }
            return -1;
        } catch (XPathExpressionException e) {
            LOG.error(e, e);
        }
        return -1;
    }

    boolean hasMatka(Document document) {
        Node node;
        try {
            node = (Node) path
                    .evaluate(
                            "id('item_list')/TABLE[@background='images/data/artifacts/zhuk3.gif']/TR/TD/@aid",
                            document, XPathConstants.NODE);
            if (node != null) {
                LOG.debug("Found: " + node.getNodeValue());
                return true;
            } else {
                LOG.debug("Not Found");
            }
        } catch (XPathExpressionException e) {
            LOG.error(e, e);
        }
        return false;
    }

    private void initTimer() {
        this.time = System.currentTimeMillis();
    }

    private boolean isTimeout(long timeout) {
        return System.currentTimeMillis() - time > timeout;
    }
}
